﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace Commands
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class RemoteCommand : Attribute
    {
        public RemoteCommand(string comment, params string[] names)
        {
            if (names == null || names.Length == 0)
                throw new ArgumentException("Input at least one name.");

            // Check that names are unique.
            if(!names.IsUnique())
                throw new ArgumentException("There are non unique names.");

            Names = new List<string>(names);
            Comment = comment;
        }

        public List<string> Names { get; set; }
        public string Comment { get; set; }

        public override string ToString()
        {
            string result = null;

            foreach (var name in Names)
                result += string.Format(" {0},", name);
            result +=  string.Format("{0}Description:{1}", Environment.NewLine, Comment);

            return result;
        }
    }

    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
    public class ParameterAttribute : Attribute
    {
        public ParameterAttribute(string comment, Type type, bool isRequired = false, params string[] names)
        {
            Debug.Assert(type != null);
            Debug.Assert(comment != null);

            Comment = comment;
            Names = names;
            this.Type = type;
            IsRequired = isRequired;
            IsInternal = false;
        }

        public ParameterAttribute(string comment, bool isRequired, IEnumerable<string> names) :
            this(comment, typeof(string), isRequired)
        {
            Names = names;
        }

        public string ToString(string tab)
        {
            if (IsInternal)
                return string.Empty;

            string result = _AddNewLineTabs("Parameter:", tab);
            foreach (var name in Names)
                result += string.Format(" {0},", name);
            result = result.ReplaceLastOccurrence(",", ".");

            result += _AddNewLineTabs(string.Format("Is required: {0}", IsRequired.ToString()), tab);
            result += _AddNewLineTabs(string.Format("Type: {0}", Type.ToString()), tab);
            result += _AddNewLineTabs(string.Format("Description: {0}", Comment), tab);

            return result + Environment.NewLine;
        }

        public Type Type { get; set; }

        public IEnumerable<string> Names { get; set; }

        public string Comment { get; set; }

        public bool IsRequired { get; set; }

        public bool IsInternal { get; protected set; }

        internal static IEnumerable<ParameterAttribute> GetMetodParameters(System.Reflection.MethodInfo method)
        {
            return GetMetodParameters(method.GetParameters());
        }

        internal static IEnumerable<ParameterAttribute> GetMetodParameters(IEnumerable<ParameterInfo> methodParameters)
        {
            var methodParametersAttributes = methodParameters.
                SelectMany(x => x.GetCustomAttributes(typeof(ParameterAttribute), false));

            return methodParametersAttributes.Cast<ParameterAttribute>();
        }

        private string _AddNewLineTabs(string text, string tab)
        {
            return Environment.NewLine + tab + text;
        }
    }

    public class RequiredParameterAttribute : ParameterAttribute
    {
        public RequiredParameterAttribute(string comment, Type type, string parameterName, params string[] names):
            base(comment, type, true)
        {
            Names = names;
        }
    }

    public class URLRequiredParameter : ParameterAttribute
    {
        public URLRequiredParameter(string comment) :
            base(comment, true, Parameters.URLParameterNames)
        {
        }
    }

    public class DirectoryParameterAttribute : ParameterAttribute
    {
        public DirectoryParameterAttribute(string comment, bool isRequired):
            base(comment, isRequired, Parameters.DirectoryParameterNames)
        {
        }
    }

    /// <summary>
    /// Folder where attached parameters has been saved. Internal parameter can be set via e-mail.
    /// </summary>
    public class FolderWithSavedAttachesParameter : ParameterAttribute
    {
        public FolderWithSavedAttachesParameter() :
            base("Folder where attached parameters has been saved.", false, new string[]{ PARAMETER_NAME})
        {
            IsInternal = true;
        }

        public const string PARAMETER_NAME = @"folderWithSavedAttaches";
    }

    public class MailAddressParameterAttribute : ParameterAttribute
    {
        public MailAddressParameterAttribute(): 
            this("Mail addres to send command work result")
        {
        }

        public MailAddressParameterAttribute(string comment):
            base(string.Format("{0}, if no such parameter - send to address from which command come.", comment),
                false, Parameters.MailParameterNames)
        {
        }
    }

    public class BoolParameterAttribute : ParameterAttribute
    {
        public BoolParameterAttribute(string commentIfParameterPresent, 
            string commentIfParameterDoesntPresent, 
            params string[] names) :
            base(string.Empty, typeof(bool), false)
        {
            Comment = string.Format("If parameter exist - {0}, otherwise - {1}.", 
                commentIfParameterPresent, commentIfParameterDoesntPresent);
            Names = names;
        }

    }

    public class PasswordParameter : ParameterAttribute
    {
        public PasswordParameter()
            : this(" - archive will be without password.")
        {
        }

        public PasswordParameter(string commentInCaseOfNoValue) :
            base(string.Format("Password, if empty, default password will be applied, if no such parameters {0}", commentInCaseOfNoValue),
            false, Parameters.PasswordParameterNames)
        {
        }
    }
}
